#include "../include/tokenize.h"
#include "../include/environ.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

char* buffer=  NULL; // contiendra la ligne de commande
int pos = 0;
/* Teste si le caractère est un entier $i ou un # $# */
int isArg(char letter) {
    if(letter == '#') {
        return 1;
    }
    else {
        char str[2];
        strcpy(str, "");
        char strLetter[2];
        strLetter[0] = letter;
        strLetter[1] = '\0';
        strcat(str, strLetter);
        int val = atoi((const char*)str);
        if(val <= 9 && val > 0) return 1;
    }
    return 0;
}

/* Renvoie 1 si le caractère passé en paramètre est un type d'espace */
int isIFS(char letter) {
    char * ifs = get_environ("IFS");
    int i = 0;
    while(i < strlen(ifs)) {
        if(ifs[i++] == letter) return 1;     
    }
    return 0;
}

/* Fonction introduisant des lettres dans str jusqu'à delimiter*/
char* until(char* str, char delimiter) {
    char letter;
    char strletter[2];
    strletter[1] = '\0';
    while(pos < strlen(buffer)) {
        letter = buffer[pos++];
        if(strcmp(str,"#") == 0) { // Cas des commentaires
            if(letter == delimiter) return NULL;
        } 
        else if(delimiter == '$') {
            strletter[0] = letter;
            if(isArg(letter)) {// $# $i i de 1 a 9
                char strBis[5];
                strcpy(strBis, "\0");
                strcat(strBis, "$");
                strcat(strBis, strletter);
                return get_environ(strBis);
            } else { // autre cas $PWD, $HOME....
                strcat(str, strletter);
                if((pos < strlen(buffer) && isIFS(buffer[pos])) || buffer[pos] == '\0') {
                    return get_environ(str);
                }
           }
        } 
        else { // guillement ou quote
             if(letter == '\\') {
               if(pos >= strlen(buffer)) return NULL;
               letter = buffer[pos++];
            } 
            strletter[0] = letter;
            strcat(str, strletter);
            if(pos < strlen(buffer) && buffer[pos] == delimiter) { 
                ++pos;
                return str;
            }
        }
    }
    return NULL;
}

/* nexPos dit si le prochain caractere 
passé en paramètre est un mot clef ou pas*/
int nextPos(char letter) {
    switch(letter) {
        case '<' :
        case '>' :
        case ';' :
        case '|' :
        case '&' :
        case '#' :
        case '$' :
        case '\n':
        case '\0':
            return 1;
    }
    return 0;
}

/* Trouver le prochain token dans line 
 * Cette  fonction a un comportement proche de celui de la fonction 
 * standard strtok:
 *     - quand la fonction est appelée avec line non NULL, on renvoie 
 *       le premier token de line.
 *     - quand line est NULL, on continue de là où l'on s'était arrêté 
 *       précédemment (i.e. on renvoie le token suivant). 
 */
struct token *get_token(char *line) {
    struct token* retour = (struct token*)malloc(sizeof(struct token));
    retour->content = NULL; 
    retour->type = error;
    // initialisation
    if(line != NULL) { // Premier appel a get_token
        buffer = (char*) malloc(sizeof(char)*MAX_TOKEN);
        strcpy(buffer, "\0");
        strcpy(buffer, line);
        pos = 0;
    } 
    char letter;
    char strletter[2];
    strletter[1] = '\0';
    char* strItem = (char*) malloc(sizeof(char)*MAX_TOKEN);
    strcpy(strItem, "\0");
    while(pos < (int)strlen(buffer)) {
        letter = buffer[pos++];
        if(!isIFS(letter)) { // item
           switch(letter) {
             case '<': 
                retour->type = less;
                free(strItem);
                return retour;
             case '>': 
                if(buffer[pos] == '>') { // cas >>
                   ++pos;
                   retour->type = append;
                } else {
                    retour->type = greater;
                }
                free(strItem);
                
                return retour;
             case ';': 
                retour->type = semicolon;
                free(strItem);
                return retour;
             case '|': 
                retour->type = pip;
                free(strItem);
                return retour;
             case '&': 
               retour->type = amper;
               free(strItem);
               return retour;
             case '#': // de # a la fin de la ligne
               until("#", '\n');
               break;
             case '$': 
               retour->content = until(strItem, '$');
               if(retour->content == NULL)
                 retour->type = error;
               else
                retour->type = item;
               return retour;
             case '\"':
               strItem = until(strItem, '\"');
               if(strItem == NULL) {
                   retour->type = error;
                   free(strItem);
               } else {
                   retour->type = item;
                   retour->content = strItem;
               }
               return retour;
             case '\'':
               strItem = until(strItem, '\'');
               if(strItem == NULL) {
                   retour->type = error;
                   free(strItem);
               } else {
                   retour->type = item;
                   retour->content = strItem;
               }
               return retour;
             default: 
               // Cas normal : ls -l
               strletter[0] = letter;
               strcat(strItem, strletter);
               if(nextPos(buffer[pos]) == 1) {
                  retour->type = item;
                  retour->content = strItem;
                  return retour;  
                } 
                     
               break;  
           }
        } else { // Cas espace
            if(strlen(strItem) == 0) continue; // chaine temporaire vide, on ajoute rien car c'est un espace
            retour->type = item;
            retour->content = strItem;
            return retour;
        }
    }
    // On a tout lu, on s'arrète
    free(strItem);
    retour->type = eol;
    return retour;
}


